/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * Copyright (C) 2024-2025 Jim Gao <gaozhsoft@aliyun.com>
 *
 */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/dma-mapping.h>
#include <linux/of_platform.h>

#include "ioctl_timer.h"

#define DRV_MSLEEP              (20)

#define SIGNAL_NUM 44  // 自定义信号编号，通常在SIGRTMIN和SIGRTMAX之间

static int chardrv_dev_major = -1;
static const char chardrv_dev_name[] = "ioctl_timer_dev";
static struct class 	*chardrv_class 	= NULL;
static struct device	*chardrv_dev 	= NULL;
volatile int audio_wirte_flag = 0; //0:not write flag; 1:write flag
volatile int timer_run_flag = 0; //0:not run flag; 1:run flag
volatile int timer_run_cnt = 0;
volatile int callback_run_flag = 0; //0:not run flag; 1:run flag

struct timer_list drv_timer;	/* timer callback for checking ack of stop request */
struct mutex my_mutex;

uint32_t g_read_elf_pid = 0;

dma_addr_t read_dma_bus_src;
uint8_t *g_read_data = NULL;

dma_addr_t write_dma_bus_src;
uint8_t *g_write_data = NULL;

uint32_t g_data_size = 0;

static void show_uint8_content(uint8_t *data, uint32_t size)
{
    printk("show data :");
    for(int i = 0; i < size; i ++)
    {
        printk("%d ", data[i]);
    }
    printk("\n");
}
/* timer callback for checking the ack of stop request */
static void drv_timer_callback(struct timer_list *t)
{

	mutex_lock(&my_mutex);
    printk("[%s +%d %s] timer_run_cnt=%d\n", __FILE__, __LINE__, __func__, timer_run_cnt);
	callback_run_flag = 1;
    printk("[%s +%d %s] callback_run_flag=%d\n", __FILE__, __LINE__, __func__, callback_run_flag);

    if(0 == timer_run_flag)
    {
        printk("[%s +%d %s] timer_run_flag=%d\n", __FILE__, __LINE__, __func__, timer_run_flag);

    }
    else
    {
        printk("[%s +%d %s] timer_run_flag=%d\n", __FILE__, __LINE__, __func__, timer_run_flag);
        {
            struct pid *pid_struct;
            struct task_struct *task_struct;
            struct kernel_siginfo info;
            printk("[%s +%d %s] g_read_elf_pid=%d\n", __FILE__, __LINE__, __func__, g_read_elf_pid);

            pid_struct = find_get_pid(g_read_elf_pid);
            if (!pid_struct) {
                printk(KERN_INFO "signal_example: Invalid PID\n");
                return;
            }
        
            task_struct = pid_task(pid_struct, PIDTYPE_PID);
            if (!task_struct) {
                printk(KERN_INFO "signal_example: Could not find task\n");
                return;
            }
            printk("[%s +%d %s] SIGNAL_NUM=%d\n", __FILE__, __LINE__, __func__, SIGNAL_NUM);

            memset(&info, 0, sizeof(struct kernel_siginfo));
            info.si_signo = SIGNAL_NUM;
            info.si_code = SI_QUEUE;
            info.si_int = 1234;  // 传递给用户空间的数据
            send_sig_info(SIGNAL_NUM, &info, task_struct);
        }

        mod_timer(&drv_timer, jiffies + msecs_to_jiffies(DRV_MSLEEP));
        timer_run_cnt ++;
    }

	callback_run_flag = 0;
    printk("[%s +%d %s] callback_run_flag=%d\n", __FILE__, __LINE__, __func__, callback_run_flag);
	mutex_unlock(&my_mutex);
}
static void *tls_dma_alloc(struct device *chardrv_dev, uint32_t size, dma_addr_t *dma_bus, uint32_t flag)
{
	void * data = NULL;
	if(0 == size)
	{
		printk("[%s +%d %s] 0 == size failed\n", __FILE__, __LINE__, __func__);
		return NULL;
	}
	data = kmalloc(size, flag);
    if (NULL == data)
    {
        printk("[%s +%d %s] kmalloc(%d) failed\n", __FILE__, __LINE__, __func__, size);
		return NULL;
    }
	return data;
}
static void tls_dma_free(struct device *chardrv_dev, uint32_t size, void *data, dma_addr_t dma_bus)
{
    if (NULL != data)
    {
    	printk("[%s +%d %s]  kfree(data)\n", __FILE__, __LINE__, __func__);
        kfree(data);
		data = NULL;
    }
}
static int ioctl_timer_open(struct inode *inode, struct file *file)
{
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);

    printk("[%s +%d %s] timer_run_flag=%d\n", __FILE__, __LINE__, __func__, timer_run_flag);
    printk("[%s +%d %s] callback_run_flag=%d\n", __FILE__, __LINE__, __func__, callback_run_flag);

	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
	return 0;
}

static int ioctl_timer_close(struct inode *inode, struct file *file)
{
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
    printk("[%s +%d %s] timer_run_flag=%d\n", __FILE__, __LINE__, __func__, timer_run_flag);
    printk("[%s +%d %s] callback_run_flag=%d\n", __FILE__, __LINE__, __func__, callback_run_flag);

	while(1)
	{
		if(0 == callback_run_flag)
		{
			break;
		}
		udelay(1);
	}
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
	return 0;
}
static int tls_dma_write_to_pl(uint8_t *g_write_data, uint32_t size)
{
    if((NULL == g_write_data) || (0 == size))
    {
        printk("[%s %d] TLS_AUDIO_IOCTL_WRITE (NULL == g_write_data) || (0 == size) failed\n", __func__, __LINE__);
        return -1;
    }
    
    printk("[%s +%d %s] audio_wirte_flag=%d\n", __FILE__, __LINE__, __func__, audio_wirte_flag);
    if(0 == audio_wirte_flag)
    {
        printk("[%s +%d %s] 0==audio_wirte_flag, then NOT \n", __FILE__, __LINE__, __func__);
    }
    else
    {
        show_uint8_content(g_write_data, 16);
        printk("[%s +%d %s] 0!=audio_wirte_flag, then add some code ps write to pl by DMA.\n", __FILE__, __LINE__, __func__);
    }

    return 0;
}

static ssize_t char_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *offset)
{
    printk("[%s +%d %s] nbytes=%ld timer_run_cnt=%d\n", __FILE__, __LINE__, __func__, nbytes, timer_run_cnt);
    for(int i = 0; i < 32; i++)
    {
        g_read_data[i] = timer_run_cnt;
    }
	if (copy_to_user(buf, g_read_data, nbytes))
	{
		return -EFAULT;
	}
	return 0;
}
static ssize_t char_drv_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *offset)
{
    int ret = 0;
    printk("[%s +%d %s] nbytes=%ld\n", __FILE__, __LINE__, __func__, nbytes);

    ret = copy_from_user(g_write_data, buf, nbytes);
    if (ret)
    {
        printk("[%s %d] TLS_AUDIO_IOCTL_WRITE copy_from_user failed\n", __func__, __LINE__);
        return -EFAULT;
    }
    audio_wirte_flag = 1;
    tls_dma_write_to_pl(g_write_data, nbytes);

	return 0;
}

static ssize_t TLS_audio_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
    int ret = 0;

    /*
     * extract the type and number bitfields, and don't decode
     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
     */
    if (_IOC_TYPE(cmd) != TLS_AUDIO_IOCTL_MAGIC) return -ENOTTY;
    if (_IOC_NR(cmd) > TLS_AUDIO_IOCTL_MAXNR) return -ENOTTY;

#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
    if (_IOC_DIR(cmd) & _IOC_READ)
        ret = !access_ok(VERIFY_WRITE, arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        ret = !access_ok(VERIFY_READ, arg, _IOC_SIZE(cmd));
#else
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
    if (_IOC_DIR(cmd) & _IOC_READ)
    {
        ret = !access_ok(&arg, _IOC_SIZE(cmd));
    }
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
    {
        ret = !access_ok(&arg, _IOC_SIZE(cmd));
    }
#endif
    if (!ret)
    {
        return -EFAULT;
    }
	printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);

    switch (cmd) {
    case TLS_AUDIO_IOCTL_INIT_ASYNC_READ:
    {
        CONFIG_T config;

        ret = copy_from_user(&config, (CONFIG_T *)arg, sizeof(CONFIG_T));
        if (ret)
        {
            printk("[%s %d] TLS_AUDIO_IOCTL_INIT_ASYNC_READ copy_from_user failed\n", __func__, __LINE__);
            return -EFAULT;
        }
        printk("[%s +%d %s] config.pid=%d, config.size=%d\n", __FILE__, __LINE__, __func__, config.pid, config.size);

        g_read_data = tls_dma_alloc(chardrv_dev, config.size, &read_dma_bus_src, GFP_KERNEL|GFP_DMA);
        if(NULL == g_read_data)
        {
            printk(KERN_INFO"alloc g_read_data buffer error\n");
            return -EFAULT;
        }
        g_write_data = tls_dma_alloc(chardrv_dev, config.size, &write_dma_bus_src, GFP_KERNEL|GFP_DMA);
        if(NULL == g_write_data)
        {
            printk(KERN_INFO"alloc g_write_data buffer error\n");
            return -EFAULT;
        }





        g_data_size = config.size;
        g_read_elf_pid = config.pid;
        timer_run_flag = 1;
        mod_timer(&drv_timer, jiffies + msecs_to_jiffies(DRV_MSLEEP));




        break;
    }
    case TLS_AUDIO_IOCTL_READ:
    {

        break;
    }
    case TLS_AUDIO_IOCTL_STOP_READ:
    {
        unsigned long flag;

        ret = copy_from_user(&flag, (CONFIG_T *)arg, sizeof(CONFIG_T));
        if (ret)
        {
            printk("[%s %d] TLS_AUDIO_IOCTL_STOP_READ copy_from_user failed\n", __func__, __LINE__);
            return -EFAULT;
        }
        mutex_lock(&my_mutex);
        printk("[%s +%d %s] timer_run_flag=%d, flag=%ld\n", __FILE__, __LINE__, __func__, timer_run_flag, flag);
        timer_run_flag = flag;
        mutex_unlock(&my_mutex);
        break;
    }
    case TLS_AUDIO_IOCTL_WRITE:
    {

        break;
    }
    case TLS_AUDIO_IOCTL_STOP_WRITE:
    {
        unsigned long flag;

        ret = copy_from_user(&flag, (CONFIG_T *)arg, sizeof(CONFIG_T));
        if (ret)
        {
            printk("[%s %d] TLS_AUDIO_IOCTL_STOP_READ copy_from_user failed\n", __func__, __LINE__);
            return -EFAULT;
        }
        mutex_lock(&my_mutex);
        printk("[%s +%d %s] timer_run_flag=%d, flag=%ld\n", __FILE__, __LINE__, __func__, timer_run_flag, flag);
        timer_run_flag = flag;
        mutex_unlock(&my_mutex);
        break;
    }
    #if 0
    case TLS_AUDIO_IOCTL_START:
    {
		timer_run_flag = 1;
		printk("[%s %d] TLS_AUDIO_IOCTL_START \n", __func__, __LINE__);

    }
    break;
    case TLS_AUDIO_IOCTL_STOP:
    {
		printk("[%s %d] TLS_AUDIO_IOCTL_STOP timer_run_flag=%d\n", __func__, __LINE__, timer_run_flag);
		timer_run_flag = 0;
    }
    break;
    case TLS_AUDIO_IOCTL_GET_BUFFER:
    {
        #if 1
        int ret = 0;
        PcmDataParams memparams;

        ret = copy_from_user(&memparams, (PcmDataParams *)arg, sizeof(PcmDataParams));
        if (ret) break;
    	printk("[%s %d] TLS_AUDIO_IOCTL_GET_BUFFER memparams.bytes=%d\n", __func__, __LINE__, memparams.bytes);


        ret |= copy_to_user((PcmDataParams *)arg, &memparams, sizeof(PcmDataParams));

        #endif
    }
    break;
    case TLS_AUDIO_IOCTL_FREE_BUFFER:
    {
        #if 1
        int ret = 0;
        PcmDataParams memparams;

        ret = copy_from_user(&memparams, (PcmDataParams *)arg, sizeof(PcmDataParams));
        if (ret) break;
    	printk("[%s %d] TLS_AUDIO_IOCTL_FREE_BUFFER memparams.bytes=%d\n", __func__, __LINE__, memparams.bytes);


        ret |= copy_to_user((PcmDataParams *)arg, &memparams, sizeof(PcmDataParams));

        #endif
    }
    break;
    #endif
    default:
        break;
    }

    return ret ? -EFAULT : 0;
}

static const struct file_operations ioctl_timer_fops = {
	.owner 		= THIS_MODULE,
	.open 		= ioctl_timer_open,
	.release 	= ioctl_timer_close,
	.read 		= char_drv_read,
	.write 		= char_drv_write,
	.unlocked_ioctl	= TLS_audio_ioctl,
};

static int __init ioctl_timer_init(void)
{
    int ret = 0;
    printk("[%s +%d %s] chardrv_dev_name=%s\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
    //printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
    chardrv_dev_major = register_chrdev(0, chardrv_dev_name, &ioctl_timer_fops);
	if(chardrv_dev_major < 0)
	{
		ret = chardrv_dev_major;
		printk("[%s +%d %s] register_chrdev(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_register_chrdev;
	}
    chardrv_class = class_create(THIS_MODULE, chardrv_dev_name);
	if(IS_ERR(chardrv_class))
	{
		printk("[%s +%d %s] class_create(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_class_create;
	}

    chardrv_dev = device_create(chardrv_class, NULL, MKDEV(chardrv_dev_major, 0), NULL, chardrv_dev_name);
	if(IS_ERR(chardrv_class))
	{
		printk("[%s +%d %s] device_create(%s) failed\n", __FILE__, __LINE__, __func__, chardrv_dev_name);
		goto err_device_create;
	}
    timer_setup(&drv_timer, drv_timer_callback, 0);
	mutex_init(&my_mutex);

	printk("[%s +%d %s] end\n", __FILE__, __LINE__, __func__);
	return 0;
    device_destroy(chardrv_class, MKDEV(chardrv_dev_major, 0));
err_device_create:
	class_destroy(chardrv_class);
err_class_create:
	unregister_chrdev(chardrv_dev_major, chardrv_dev_name);
err_register_chrdev:
    return 0;
}

static void __exit ioctl_timer_exit(void)
{
    int ret = 0;
    if(NULL != g_read_data)
    {
        printk("[%s +%d %s] if  NULL != g_read_data\n", __FILE__, __LINE__, __func__);
        tls_dma_free(chardrv_dev, g_data_size, g_read_data, read_dma_bus_src);
        g_read_data = NULL;
    }
    else
    {
        printk("[%s +%d %s] else NULL != g_read_data\n", __FILE__, __LINE__, __func__);

    }
    if(NULL != g_write_data)
    {
        printk("[%s +%d %s] if  NULL != g_write_data\n", __FILE__, __LINE__, __func__);
        tls_dma_free(chardrv_dev, g_data_size, g_write_data, read_dma_bus_src);
        g_write_data = NULL;
    }
    else
    {
        printk("[%s +%d %s] else NULL != g_write_data\n", __FILE__, __LINE__, __func__);

    }
    ret = del_timer(&drv_timer);
    if (ret)
    {
        printk("[%s +%d %s] del_timer(&drv_timer) failed\n", __FILE__, __LINE__, __func__);
    }
    mutex_destroy(&my_mutex);
    device_destroy(chardrv_class, MKDEV(chardrv_dev_major, 0));
    class_destroy(chardrv_class);
    unregister_chrdev(chardrv_dev_major, chardrv_dev_name);
    printk("[%s +%d %s] \n", __FILE__, __LINE__, __func__);
}

module_init(ioctl_timer_init);
module_exit(ioctl_timer_exit);

MODULE_AUTHOR("Jim Gao <gaozhsoft@aliyun.com>");
MODULE_DESCRIPTION("basic example for ioctl timer driver");
MODULE_LICENSE("GPL v2");

